/*
 * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

/* I2S Digital Microphone Recording Example */
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "format_wav.h"
#include "driver/i2c_master.h"
#include "xl9555.h"
#include "driver/i2s_pdm.h"

static const char *TAG = "pdm_rec_example";
//数据位深
#define BIT_SAMPLE          16
//I2S采样率
#define SAMPLE_RATE         24000

#define SPI_DMA_CHAN        SPI_DMA_CH_AUTO
#define NUM_CHANNELS        (1) // 通道数
#define SD_MOUNT_POINT      "/sdcard"   //sd卡挂载点
#define SAMPLE_SIZE         (BIT_SAMPLE * 1024)
#define BYTE_RATE           (SAMPLE_RATE * (BIT_SAMPLE / 8)) * NUM_CHANNELS



// When testing SD and SPI modes, keep in mind that once the card has been
// initialized in SPI mode, it can not be reinitialized in SD mode without
// toggling power to the card.
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
sdmmc_card_t *card;
i2s_chan_handle_t tx_handle = NULL;
i2s_chan_handle_t rx_handle = NULL;

static i2c_master_bus_handle_t ret_bus_handle;
static int16_t i2s_readraw_buff[SAMPLE_SIZE];
size_t bytes_read;
const int WAVE_HEADER_SIZE = 44;

#define TF_CS       GPIO_NUM_4
#define TF_MOSI     GPIO_NUM_5
#define TF_MISO     GPIO_NUM_7
#define TF_SCK      GPIO_NUM_6

#define I2S_BCLK    GPIO_NUM_46
#define I2S_WS      GPIO_NUM_9
#define I2S_SDOUT   GPIO_NUM_8
#define SPK_EN_IO           IO0_0

#define PDM_CLK     GPIO_NUM_3
#define PDM_DATA    GPIO_NUM_42


#define XL9555_SDA  GPIO_NUM_10
#define XL9555_SCL  GPIO_NUM_11


void mount_sdcard(void)
{
    esp_err_t ret;
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        .max_files = 5,
        .allocation_unit_size = 8 * 1024
    };
    ESP_LOGI(TAG, "Initializing SD card");

    spi_bus_config_t bus_cfg = {
        .mosi_io_num = TF_MOSI,
        .miso_io_num = TF_MISO,
        .sclk_io_num = TF_SCK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000,
    };
    ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize bus.");
        return;
    }
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = TF_CS;
    slot_config.host_id = host.slot;

    ret = esp_vfs_fat_sdspi_mount(SD_MOUNT_POINT, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return;
    }
    sdmmc_card_print_info(stdout, card);
}


void init_speaker()
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
    i2s_std_config_t i2s_tx_cfg ={
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,
            .bclk = I2S_BCLK,
            .ws = I2S_WS,
            .dout = I2S_SDOUT,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };
    //i2s_tx_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT;
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &i2s_tx_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
}

void init_pdm_microphone(void)
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));

    i2s_pdm_rx_config_t pdm_rx_cfg = {
        .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        /* The default mono slot is the left slot (whose 'select pin' of the PDM microphone is pulled down) */
        .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .clk = PDM_CLK,
            .din = PDM_DATA,
            .invert_flags = {
                .clk_inv = false,
            },
        },
    };
    pdm_rx_cfg.slot_cfg.slot_mask = I2S_PDM_SLOT_RIGHT;
    ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_handle, &pdm_rx_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
}

void record_wav(uint32_t rec_time)
{
    int flash_wr_size = 0;
    ESP_LOGI(TAG, "Opening file");
    uint32_t flash_rec_time = BYTE_RATE * rec_time;
    const wav_header_t wav_header =
        WAV_HEADER_PCM_DEFAULT(flash_rec_time, BIT_SAMPLE, SAMPLE_RATE, 1);
    struct stat st;
    if (stat(SD_MOUNT_POINT"/record.wav", &st) == 0) {
        unlink(SD_MOUNT_POINT"/record.wav");
    }
    
    FILE *f = fopen(SD_MOUNT_POINT"/record.wav", "a");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }

    // Write the header to the WAV file
    fwrite(&wav_header, sizeof(wav_header), 1, f);
    // Start recording
    while (flash_wr_size < flash_rec_time) {
        // Read the RAW samples from the microphone
        if (i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 2000) == ESP_OK) {
           for(int i = 0;i<bytes_read/2;i++)
           {
                i2s_readraw_buff[i] = i2s_readraw_buff[i]<<2;
           }
           printf("[0] %d [1] %d [2] %d [3]%d ...\n", i2s_readraw_buff[0], i2s_readraw_buff[1], i2s_readraw_buff[2], i2s_readraw_buff[3]);
           printf("[4] %d [5] %d [6] %d [7]%d ...\n", i2s_readraw_buff[4], i2s_readraw_buff[5], i2s_readraw_buff[6], i2s_readraw_buff[7]);
           printf("[8] %d [9] %d [10] %d [11]%d ...\n", i2s_readraw_buff[8], i2s_readraw_buff[9], i2s_readraw_buff[10], i2s_readraw_buff[11]);
           printf("[12] %d [13] %d [14] %d [15]%d ...\n", i2s_readraw_buff[12], i2s_readraw_buff[13], i2s_readraw_buff[14], i2s_readraw_buff[15]);
            fwrite(i2s_readraw_buff, bytes_read, 1, f);
            flash_wr_size += bytes_read;
        } else {
            printf("Read Failed!\n");
        }
    }

    ESP_LOGI(TAG, "Recording done!");
    fclose(f);
                         
    
}

bool verify_wav(wav_header_t *wav_header)
{
    if(memcmp(wav_header->descriptor_chunk.chunk_id,"RIFF",0) != 0)
    {
        ESP_LOGI(TAG,"wav file chunk id invaild!,value is %s",wav_header->descriptor_chunk.chunk_id);
        return false;
    }
    if(memcmp(wav_header->fmt_chunk.subchunk_id,"fmt",0) != 0)
    {
        ESP_LOGI(TAG,"wav file chunk id invaild!,value is %s",wav_header->fmt_chunk.subchunk_id);
        return false;
    }
    if(memcmp(wav_header->data_chunk.subchunk_id,"data",0) != 0)
    {
        ESP_LOGI(TAG,"wav file chunk id invaild!,value is %s",wav_header->data_chunk.subchunk_id);
        return false;
    }
    return true;
}

void play_wav(void)
{
    const size_t i2s_tx_buf_len = 4096;
    size_t sdcard_rx_len = 0;
    FILE *f = fopen(SD_MOUNT_POINT"/record.wav", "r");
    if(!f)
    {
        ESP_LOGI(TAG,"record.wav open fail!");
        return;
    }
    wav_header_t wav_header;
    fread(&wav_header,sizeof(wav_header),1,f);
    if(!verify_wav(&wav_header))
    {
        fclose(f);
        return;
    }
    uint8_t *read_buf = malloc(i2s_tx_buf_len);
    if(!read_buf)
    {
        fclose(f);
        return;
    }
    do
    {
        fread(read_buf,i2s_tx_buf_len,1,f);
        i2s_channel_write(tx_handle,read_buf,i2s_tx_buf_len,NULL,200);
        sdcard_rx_len += i2s_tx_buf_len;
        vTaskDelay(pdMS_TO_TICKS(10));
    } while (sdcard_rx_len < wav_header.data_chunk.subchunk_size);
    free(read_buf);
    fclose(f);
}

void unmount_sdcard(void)
{
    ESP_LOGI(TAG, "File written on SDCard");
    // All done, unmount partition and disable SPI peripheral
    esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, card);
    ESP_LOGI(TAG, "Card unmounted");
    // Deinitialize the bus after all devices are removed
    spi_bus_free(host.slot);
}



void i2c_and_xl9555_init(void)
{
    i2c_master_bus_config_t bus_config = 
    {
        .i2c_port = 1,
        .sda_io_num = XL9555_SDA,
        .scl_io_num = XL9555_SCL,
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .trans_queue_depth = 0,
        .flags.enable_internal_pullup = 1,
    };
    
    i2c_new_master_bus(&bus_config,&ret_bus_handle);
    xl9555_init(ret_bus_handle,GPIO_NUM_NC,NULL);
    xl9555_ioconfig((~(SPK_EN_IO))&0xFFFF);

}


void app_main(void)
{
    printf("Microphone recording example start\n--------------------------------------\n");
    mount_sdcard();
    vTaskDelay(pdMS_TO_TICKS(500));
    init_pdm_microphone();
    init_speaker();
    i2c_and_xl9555_init();
    ESP_LOGI(TAG, "Starting recording for %d seconds!", 10);
    record_wav(10);
    xl9555_pin_write(SPK_EN_IO,1);
    vTaskDelay(pdMS_TO_TICKS(100));
    ESP_LOGI(TAG, "Starting play wav");
    play_wav();
    unmount_sdcard();
    ESP_ERROR_CHECK(i2s_channel_disable(rx_handle));
    ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
    ESP_ERROR_CHECK(i2s_del_channel(rx_handle));
    ESP_ERROR_CHECK(i2s_del_channel(tx_handle));
    xl9555_pin_write(SPK_EN_IO,0);
    while(1)
        vTaskDelay(pdMS_TO_TICKS(1000));
    return;
}
